package org.bee.controller;

import jakarta.servlet.http.HttpServletRequest;
import org.bee.PageUtil;
import org.bee.ResultTool;
import org.bee.check.ModelCheck;
import org.bee.check.VoCheck;
import org.bee.check.model.CheckResult;
import org.bee.cryptix.BCryptUtils;
import org.bee.enums.ResultCode;
import org.bee.exception.ExceptionUtils;
import org.bee.id.UUIDUtils;
import org.bee.input.ChangePasswordVo;
import org.bee.input.DictSelectVo;
import org.bee.input.LoginVo;
import org.bee.input.MyUpdate;
import org.bee.input.OnlineUserSearchVo;
import org.bee.json.JsonUtils;
import org.bee.model.AdminUser;
import org.bee.model.DictSelect;
import org.bee.model.Element;
import org.bee.model.Permission;
import org.bee.model.result.Result;
import org.bee.output.LoginSuccess;
import org.bee.output.Menus;
import org.bee.redis.RedisUtils;
import org.bee.secuirty.annotation.ApiAuth;
import org.bee.secuirty.annotation.TokenIgnore;
import org.bee.secuirty.context.CurrentUser;
import org.bee.secuirty.model.TokenInfo;
import org.bee.secuirty.model.UserInfo;
import org.bee.service.AdminUserService;
import org.bee.service.ElementService;
import org.bee.service.ext.AdminUserExtService;
import org.bee.service.ext.PermissionExtService;
import org.bee.string.StringUtils;
import org.bee.transaction.TransUtils;
import org.bee.transaction.Transaction;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 登录控制器
 */
@RestController
public class LoginController {

    private final AdminUserExtService adminUserExtService;
    private final AdminUserService adminUserService;
    private final PermissionExtService permissionExtService;
    private final ElementService elementService;
    private final RedisUtils redisUtils;

    public LoginController(AdminUserExtService adminUserExtService,
                           AdminUserService adminUserService,
                           PermissionExtService permissionExtService,
                           ElementService elementService,
                           RedisUtils redisUtils) {
        this.adminUserExtService = adminUserExtService;
        this.adminUserService = adminUserService;
        this.permissionExtService = permissionExtService;
        this.elementService = elementService;
        this.redisUtils = redisUtils;
    }

    @Value("${expiredTime}")
    private Integer expiredTime;

    /**
     * 系统用户登录(不鉴权)
     * @param user 用户信息
     * @return 登录结果
     */
    @TokenIgnore
    @PostMapping("login")
    public Result login(@RequestBody LoginVo user) {
        //参数校验
        Result result = VoCheck.check(user);
        if (!result.getSuccess()) return result;
        //查询用户信息
        AdminUser adminUser;
        List<AdminUser> userByUserName = adminUserExtService.getUserByUserName(user.getUsername());
        if(null == userByUserName || userByUserName.size() != 1){
            result = ResultTool.fail(ResultCode.USER_ACCOUNT_ERROR);
            result.setMsg("用户名或密码错误");
            return result;
        }
        adminUser = userByUserName.getFirst();
        //登录校验
        result = adminUserExtService.loginCheck(user, adminUser);
        if (!result.getSuccess()) return result;

        TokenInfo tokenInfo = new TokenInfo();
        String tokenKey = (String) redisUtils.get("LoginUsers:"+user.getUsername());
        //登录成功后返回一个token,token包含一些用户常规信息
        UserInfo userInfo = new UserInfo();
        BeanUtils.copyProperties(adminUser, userInfo);
        tokenInfo.setUserInfo(userInfo);
        //查用户拥有的api权限信息
        List<Permission> permissionByUserId = permissionExtService.getPermissionByUserId(adminUser.getUserId());
        List<String> userApis = new ArrayList<>();
        for (Permission permission : permissionByUserId) {
            String code = permission.getCode();
            userApis.add(code);
        }
        tokenInfo.setUserApis(userApis);

        //查用户拥有的菜单权限信息
        List<Element> elementByUserId = elementService.getMenusByUserId(adminUser.getUserId());

        List<String> userElements = new ArrayList<>();
        for (Element element : elementByUserId) {
            String code = element.getCode();
            userElements.add(code);
        }
        tokenInfo.setUserElements(userElements);
        if (null != tokenKey) {
            //用户已经登录过了
            //重置过期时间
            redisUtils.expire("LoginUserTokenInfo:" + tokenKey, expiredTime);
        } else {
            //生成令牌
            tokenKey = UUIDUtils.simpleUuid();
            //转字符串
            String value = JsonUtils.toJson(tokenInfo);
            if(null==value){
                result = ResultTool.fail(ResultCode.TO_JSON_ERROR);
                return result;
            }
            //存redis
            redisUtils.set("LoginUserTokenInfo:" + tokenKey, value, expiredTime);
            //存用户登录状态
            redisUtils.set("LoginUsers:"+user.getUsername(), tokenKey, expiredTime);
        }
        LoginSuccess loginSuccess = new LoginSuccess();
        loginSuccess.setToken(tokenKey);
        loginSuccess.setUserInfo(userInfo);
        result.setData(loginSuccess);
        return result;
    }
    /**
     * 系统用户登出
     * @param request 请求
     * @return 结果
     */
    @DeleteMapping("logout")
    public Result logout(HttpServletRequest request){
        Result result = ResultTool.success();
        String tokenInfo = request.getHeader("Authorization");
        if(tokenInfo!=null){
            //获取到了token信息（已经登录过的）
            String bearer = tokenInfo.replace("Bearer ", "");
            redisUtils.del("LoginUserTokenInfo:" + bearer);
            UserInfo user = CurrentUser.getUser();
            redisUtils.del("LoginUsers:"+user.getUsername());
        }
        return result;
    }

    /**
     * 获取用户菜单树
     * @return 菜单树
     */
    @GetMapping("getMenus")
    public Result getMenus(){
        Result result = ResultTool.success();
        UserInfo user = CurrentUser.getUser();
        List<Menus> menus = elementService.getMenusTree(user.getUserId(),null,false);
        result.setData(menus);
        return result;
    }

    /**
     * 获取用户按钮权限
     * @return 用户按钮权限
     */
    @GetMapping("getAuthElement")
    public Result getAuthElement(){
        Result result = ResultTool.success();
        UserInfo user = CurrentUser.getUser();
        HashMap<String, List<String>> authButtonByUserId = elementService.getAuthButtonByUserId(user.getUserId());
        result.setData(authButtonByUserId);
        return result;
    }
    /**
     * 修改密码
     */
    @PostMapping("changePassword")
    public Result changePassword(@RequestBody ChangePasswordVo changePasswordVo){
        Result result = ResultTool.success();
        UserInfo user = CurrentUser.getUser();
        CheckResult check = ModelCheck.check(changePasswordVo);
        if(!check.isResult()){
            result = ResultTool.fail(ResultCode.PARAM_NOT_VALID);
            result.setMsg(check.getMsg());
            return result;
        }
        List<AdminUser> userByUserName = adminUserExtService.getUserByUserName(user.getUsername());
        if(null == userByUserName || userByUserName.size() >1){
            result = ResultTool.fail(ResultCode.USER_ACCOUNT_ERROR);
            result.setMsg("用户不存在");
            return result;
        }
        AdminUser adminUser = userByUserName.getFirst();
        //密码校验
        if(!BCryptUtils.checkPW(changePasswordVo.getOldPassword(),adminUser.getPassword())){
            result = ResultTool.fail(ResultCode.USER_CREDENTIALS_ERROR);
            result.setMsg("旧密码错误");
            return result;
        }
        adminUser.setUpdateUserId(CurrentUser.getUser().getUserId());
        adminUser.setUpdateTime(new Date());
        adminUser.setPassword(BCryptUtils.encrypt(changePasswordVo.getNewPassword()));
        adminUserService.update(adminUser);
        return result;
    }
    /**
     * 修改我的基础信息
     */
    @PostMapping("changeMyInfo")
    public Result changePassword(@RequestBody MyUpdate myUpdate){
        Result result = ResultTool.success();
        CheckResult check = ModelCheck.check(myUpdate);
        if(!check.isResult()){
            result = ResultTool.fail(ResultCode.PARAM_NOT_VALID);
            result.setMsg(check.getMsg());
            return result;
        }
        Transaction trans = null;
        try {
            AdminUser adminUser = new AdminUser();
            BeanUtils.copyProperties(myUpdate, adminUser);
            adminUser.setUserId(CurrentUser.getUser().getUserId());
            adminUser.setUpdateUserId(CurrentUser.getUser().getUserId());
            adminUser.setUpdateTime(new Date());
            trans= TransUtils.getTrans();
            trans.begin();
            adminUserService.update(adminUser);
            trans.commit();
        } catch (Exception e) {
            TransUtils.rollback(trans);
            result = ResultTool.fail();
            return result;
        }
        return result;
    }

    /**
     * 获取在线用户列表
     */
    @PostMapping("getOnlineUser")
    @ApiAuth(value = "user.getOnlineUser",description = "获取在线用户列表")
    public Result getOnlineUser(@RequestBody OnlineUserSearchVo onlineUserSearchVo){
        Result result = ResultTool.success();
        try {
            PageUtil.setPage(onlineUserSearchVo.getCurrent(),onlineUserSearchVo.getSize());
            long keyCount = 1L;
            Set<String> keys = new HashSet<>();
            if(StringUtils.isEmpty(onlineUserSearchVo.getUsername())){
                keys = redisUtils.keysPage("LoginUsers:*",
                        onlineUserSearchVo.getCurrent(), onlineUserSearchVo.getSize());
                keyCount = redisUtils.getKeyCount("LoginUsers:*");
            }else{
                String key = "LoginUsers:"+onlineUserSearchVo.getUsername();
                keys.add(key);
            }

            List<DictSelect> onlineUserList = new ArrayList<>();
            for (String key : keys) {
                DictSelect user = new DictSelect();
                String userToken = (String) redisUtils.get(key);
                if(StringUtils.isNotEmpty(userToken)){
                    String label = key.replaceAll("LoginUsers:", "");
                    user.setLabel(label);
                    user.setValue(userToken);
                    onlineUserList.add(user);
                }else{
                    keyCount = 0;
                }
            }
            PageUtil.getPage().setTotal(keyCount);
            result.setPageData(onlineUserList);
        } catch (Exception e) {
            result = ResultTool.fail();
            result.setMsg("查询异常");
            return result;
        } finally {
            PageUtil.clearPage();
        }
        return result;
    }

    /**
     * 强制登出
     * @param token 令牌
     * @return 登出结果
     */
    @DeleteMapping("forceLogout")
    @ApiAuth(value = "user.forceLogout",description = "强制登出")
    public Result forceLogout(String token){
        Result result = ResultTool.success();
        String tokenInfo = (String) redisUtils.get("LoginUserTokenInfo:" + token);
        TokenInfo user = JsonUtils.fromJson(tokenInfo, TokenInfo.class);
        if(null==user){
            result = ResultTool.fail(ResultCode.DATA_NULL);
            result.setMsg("未查询到登录信息，登出失败");
            return result;
        }
        String username = user.getUserInfo().getUsername();
        redisUtils.del("LoginUserTokenInfo:" + token);
        redisUtils.del("LoginUsers:"+username);
        return result;
    }
}
